package cmd

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"gitee.com/ldysdy999/hostdemon/app"
	"gitee.com/ldysdy999/hostdemon/app/host/impl"
	"gitee.com/ldysdy999/hostdemon/conf"
	"gitee.com/ldysdy999/hostdemon/protocol"
	"github.com/infraboard/mcube/logger"
	"github.com/infraboard/mcube/logger/zap"
	"github.com/spf13/cobra"
)

var (
	configType string
	confFile   string
)

var startCmd = &cobra.Command{
	Use:   "start",
	Short: "启动Demo后端API服务",
	Long:  "启动,初始化,完成Demo后端API服务启动",
	RunE: func(cmd *cobra.Command, args []string) error {
		//加载全局配置
		if err := loadGlobalConfig(configType); err != nil {
			return err
		}

		//初始化全局日志实例
		if err := loadGlobalLogger(); err != nil {
			return err
		}

		//初始化服务层实例
		if err := impl.Service.Init(); err != nil {
			return err
		}
		//将服务实例注册给IOC层
		app.Host = impl.Service

		//监听停止服务信号
		ch := make(chan os.Signal, 1)
		signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP, syscall.SIGQUIT)

		//初始化服务
		svr := NewService(conf.C())

		go svr.waitSign(ch)

		//启动服务
		return svr.start()
	},
}

func NewService(conf *conf.Config) *Service {
	return &Service{
		conf: conf,
		http: protocol.NewHTTPService(),
		log:  zap.L().Named("service"),
	}
}

//service
//服务的整体配置
//服务可能启动有很多模块：http，grpc，contable
type Service struct {
	conf *conf.Config
	http *protocol.HTTPService
	log  logger.Logger
}

func (s *Service) start() error {
	return s.http.Start()
}

func (s *Service) stop() error {
	return s.http.Stop()
}

//当发现用户手动终止，完成相应处理
func (s *Service) waitSign(sign chan os.Signal) {
	for sg := range sign {
		switch v := sg.(type) {
		default:
			s.log.Info("receive signal '%v',start graceful shutdown", v.String())
			if err := s.http.Stop(); err != nil {
				s.log.Error("graceful shutdown err %s,force exit", err)
			}
			s.log.Info("service stop complete")
			return
		}
	}
}

// 根据配置文件，进行全局配置的加载
func loadGlobalConfig(configType string) error {
	// 配置加载
	switch configType {
	case "file":
		err := conf.LoadFromFile(confFile)
		if err != nil {
			return err
		}
	case "env":
		err := conf.LoadFromEnv()
		if err != nil {
			return err
		}
	case "etcd":
		return errors.New("not implemented")
	default:
		return errors.New("unknown config type")
	}
	return nil
}

//根据配置文件，进行log的加载
func loadGlobalLogger() error {
	var (
		logInitMsg string
		level      zap.Level
	)
	//获取日志配置对象
	lc := conf.C().Log
	//解析配置文件定义日志级别，初始化日志实例
	lv, err := zap.NewLevel(lc.Level)
	//解析失败，默认使用info级别
	if err != nil {
		logInitMsg = fmt.Sprintf("%s, use default level INFO", err)
		level = zap.InfoLevel
		//解析成功
	} else {
		level = lv
		logInitMsg = fmt.Sprintf("log level: %s", lv)
	}

	//初始化了日志默认配置
	zapConfig := zap.DefaultConfig()
	zapConfig.Level = level
	//使用false，新的日志就可以沿用上个日志文件，知道达到10M
	zapConfig.Files.RotateOnStartup = false
	switch lc.To {
	//配置文件如果是标准输出，就只打印到标准输出，不在配置文件打印了
	case conf.ToStdout:
		zapConfig.ToStderr = true
		zapConfig.ToFiles = false
	case conf.ToFile:
		zapConfig.Files.Name = "restful-api.log"
		zapConfig.Files.Path = lc.Dir
	}
	switch lc.Format {
	//默认就是文本格式，所以值判断是否是json格式就可
	case conf.JSONFormat:
		zapConfig.JSON = true
	}
	//根据日志默认配置，初始化全局Logger配置
	if err := zap.Configure(zapConfig); err != nil {
		return err
	}

	//全局Logger初始化后，就可以正常使用
	zap.L().Named("INIT").Info(logInitMsg)
	return nil
}

func init() {
	startCmd.PersistentFlags().StringVarP(&configType, "config_type", "t", "file", "the restful-api config type")
	startCmd.PersistentFlags().StringVarP(&confFile, "config_file", "f", "etc/restful-api.toml", "the restful-api config file path")
	rootCmd.AddCommand(startCmd)
}
